#ifndef _KLAS_SHADER_PROGRAM_HPP_
#define _KLAS_SHADER_PROGRAM_HPP_

#include "../Library.hpp"
#include "Shader.hpp"

namespace klas
{
  namespace graphics
  {
    class Shader;

    //! A shader program allows shaders to be used.
    /*! The shader program allows the turning on / disabling of groups of shaders.
        You can map a set of shaders to a program, and then activate said program.
        The graphics hardware will then use these shaders to render, replacing
        the "fixed" pipeline. 
        
        Before a shader program is enabled, it must be linked.
    */
    class ShaderProgram: public virtual library::Base
    {
    public:
      //! Dtor.  Destroys the shader.
      virtual ~ShaderProgram() {}

      //! Attaches a shader.  This shader needs to be linked later on.  Some shaders must be attached.
      virtual void attach(Shader* shader) = 0;

      //! Deattaches a shader from the program.
      virtual void deattach(Shader* shader) = 0;

      //! Links the shader program, this must be called before enable / disable.
      virtual void link() = 0;

      //! Enable the shaders.
      /*! Before calling this function, link() must be called.
          \sa link */
      virtual void enable() = 0;

      //! Turn of the shaders.
      virtual void disable() = 0;


      //! Sets a variable that the shader can use.  Slow version (needs to get the ID of the variable name).
      /*! In GLSL, the variable is uniform. 
          \warning The shader MUST be enabled. */
      virtual uint32 setVariable(string name, double v0) = 0;

      //! Sets a variable that the shader can use.
      /*! In GLSL, the variable is uniform. 
          \warning The shader MUST be enabled. */
      virtual uint32 setVariable(string name, double v0, double v1) = 0;

      //! Sets a variable that the shader can use.  Slow version (needs to get the ID of the variable name).
      /*! In GLSL, the variable is uniform. 
          \warning The shader MUST be enabled. */
      virtual uint32 setVariable(string name, double v0, double v1, double v2) = 0;

      //! Sets a variable that the shader can use.  Slow version (needs to get the ID of the variable name).
      /*! In GLSL, the variable is uniform. 
          \warning The shader MUST be enabled. */
      virtual uint32 setVariable(string name, double v0, double v1, double v2, double v3) = 0;

      //! Sets a variable that the shader can use.  Fast version (immediatly changes the variable)
      /*! In GLSL, the variable is uniform. 
          \warning The shader MUST be enabled.
          \return The ID of the variable. */
      virtual uint32 setVariable(uint32 id, double v0) = 0;

      //! Sets a variable that the shader can use.  Fast version (immediatly changes the variable)
      /*! In GLSL, the variable is uniform. 
          \warning The shader MUST be enabled.
          \return The ID of the variable. */
      virtual uint32 setVariable(uint32 id, double v0, double v1) = 0;

      //! Sets a variable that the shader can use.  Fast version (immediatly changes the variable)
      /*! In GLSL, the variable is uniform. 
          \warning The shader MUST be enabled.
          \return The ID of the variable. */
      virtual uint32 setVariable(uint32 id, double v0, double v1, double v2) = 0;

      //! Sets a variable that the shader can use.  Fast version (immediatly changes the variable)
      /*! In GLSL, the variable is uniform. 
          \warning The shader MUST be enabled.
          \return The ID of the variable. */
      virtual uint32 setVariable(uint32 id, double v0, double v1, double v2, double v3) = 0;

      //! Returns the ID of a variable name, and allocates room for that variable if it has not yet been created.
      virtual uint32 getID(string name) = 0;


      //! Sets a variable that the shader can use.  Fast version (immediatly changes the variable)
      /*! In GLSL, the variable is uniform. 
          \param id The id of the variable.  Can be retrived from a setVariable("str"...) or a getID("str") call.
          \param elementCount How many objects per "element."  For example, if you are sending a vector2, it would be two.
                              For a vector3, it would be 3.  It determines the "dimensions" of the object in the shader.
                              Accepted values are 1, 2, 3, and 4.
          \param totalElements How many elements should be sent.  If an array is size 18, with an x and y coordinate
                               (elementCount would be 2), totalElements would be 8.
          \warning The shader MUST be enabled.
          \return The ID of the variable. */
      virtual uint32 setVariable(uint32 id, uint8 elementCount, int totalElements, double* arr) = 0;

      //! Sets a variable that the shader can use.  Slow version (needs to get the ID of the variable)
      /*! In GLSL, the variable is uniform. 
          \param name The name of the variable in the shader.
          \param elementCount How many objects per "element."  For example, if you are sending a vector2, it would be two.
                              For a vector3, it would be 3.  It determines the "dimensions" of the object in the shader.
                              Accepted values are 1, 2, 3, and 4.
          \param totalElements How many elements should be sent.  If an array is size 18, with an x and y coordinate
                               (elementCount would be 2), totalElements would be 8.
          \warning The shader MUST be enabled.
          \return The ID of the variable. */
      virtual uint32 setVariable(string name, uint8 elementCount, int totalElements, double* arr) = 0;


      //virtual void addConstant(string name, float* data, int col = 1) = 0;
      //virtual void addConstant(string name, const math::Matrix4& m) = 0;

      //virtual void addVariable(string name, float v0) = 0;
      //virtual void addVariable(string name, float v0, float v1) = 0;
      //virtual void addVariable(string name, float v0, float v1, float v2) = 0;
      //virtual void addVariable(string name, float v0, float v1, float v2, float v3) = 0;
      //virtual void addVariable(string name, float* data, int col = 1) = 0;
      //virtual void addVariable(string name, const math::Matrix4& m) = 0;
    };
  }
}

#endif